# CodeName：Split_Mx
# Authon：liutao
# E-Mail：wxyj3496@126.com
# Creat time：2021-09-18
# Last Modified by:liutao
# Last Modified Time:2021-09-27
# futction：根据指定的路径的文件，将焊装信息填入明细表，并根据分立、贴片、三防、固封的信息分类输出BOM

# 导入模块
import xlwings as xw
import pandas as pd
from tkinter import messagebox as msg
from tkinter import ttk
import os



###############################################################################################################
# 模块内部函数
# 输入为物料编码列和库表格，比较物料编码列，提取需要的列并返回
###############################################################################################################

def comp_numb(wlbm_MX_in,df_source_in):
	#新建一个空的14列DataFrame
	df_MX_out = pd.DataFrame(columns=[0,1,2,3,4,5,6,7,8,9,10,11,12])

	#对明细表TOP的每一个物料编码与提取的库文件进行比对，形成新的dataframe
	n = ''
	for m in wlbm_MX_in:
		if m != n:
			c = df_source_in[df_source_in[0].isin([m])]
			if c.empty:
				c = pd.DataFrame([['无效','无效','无效','无效','无效','无效','无效','无效','无效',\
					'无效','无效','无效','无效']])
		else:
			if n == '':
				c = pd.DataFrame([['无效','无效','无效','无效','无效','无效','无效','无效','无效',\
					'无效','无效','无效','无效']])
		df_MX_out = pd.concat([df_MX_out,c],axis = 0)
		n = m
	#行序号重新排列
	df_MX_out.reset_index(drop=True, inplace=True)
	return df_MX_out

###############################################################################################################
# 模块内部函数
# 更改BOM的格式
###############################################################################################################
def formatset_BOM(range_excel):
	#单元格横向居中
	range_excel.api.HorizontalAlignment = -4108 # -4108 水平居中。 -4131 靠左，-4152 靠右
	#纵向自动换行
	range_excel.api.VerticalAlignment = -4130   # -4108 水平居中。 -4131 靠左，-4152 靠右
	
	#设置单元格外框线
	# Borders(7) 左边框，LineStyle = 2 虚线
	# Borders(8) 顶部框，LineStyle = 5 双点划线
	# Borders(9) 底部边框，LineStyle = 1 直线
	# Borders(10) 右边框，LineStyle = 4 点划线
	# Borders(11) 内部垂直边线
	# Borders(12) 内部水平边线

	range_excel.api.Borders(7).LineStyle = 1
	range_excel.api.Borders(7).Weight = 2
	range_excel.api.Borders(8).LineStyle = 1
	range_excel.api.Borders(8).Weight = 2
	range_excel.api.Borders(9).LineStyle = 1
	range_excel.api.Borders(9).Weight = 2
	range_excel.api.Borders(10).LineStyle = 1
	range_excel.api.Borders(10).Weight = 2
	range_excel.api.Borders(11).LineStyle = 1
	range_excel.api.Borders(11).Weight = 2
	range_excel.api.Borders(12).LineStyle = 1
	range_excel.api.Borders(12).Weight = 2




###############################################################################################################
# 模块内部函数
# 实现拆分表格的主功能
###############################################################################################################

def Split_MX(path_sourcefile, path_inputfile, path_BOMfile, path_outputforder, progress_bar):

	# 根据已知路径，获取文件名称
	a = os.path.split(path_inputfile)
	b = os.path.splitext(a[1])

	#定义输出文件路径和导入文件读取的页面
	path_MXout = path_outputforder + '/' + b[0] + '_增加焊装信息.xls'
	path_BOMout = path_outputforder + '/'+ b[0] + '_焊装BOM.xls' 
	path_NBOMout = path_outputforder + '/'+ b[0] + '_不在BOM清单中元器件.xls' 
	name_sourcesheet1 = '电子元器件'
	name_sourcesheet2 = '电子元器件1'
	name_MXT = '贴装检验T'
	name_MXB = '贴装检验B'

	#进度条更新为3%
	progress_bar['value'] = 3
	progress_bar.update()


###############################################################################################################
#提取库文件表格，形成需要的DataFrame
###############################################################################################################

	# 实例化一个excel
	app_source = xw.App(visible = False, add_book=False)
	workbook_source = app_source.books.open(path_sourcefile)

	#读库文件的连个表格，若无法读取则退出
	try:
		#读取库文件的两个表格
		worksheet_source1 = workbook_source.sheets(name_sourcesheet1)
		worksheet_source2 = workbook_source.sheets(name_sourcesheet2)
		df_sourcesheet1 = pd.DataFrame(worksheet_source1.range('A1').expand('table').value)
		df_sourcesheet2 = pd.DataFrame(worksheet_source2.range('A1').expand('table').value)
	except:
		msg.showinfo('错误提示', '库文件格式错误！')
		#进度条更新为0%
		progress_bar['value'] = 0
		progress_bar.update()
		workbook_source.close()
		app_source.quit()
		return

	#读取完库表格关闭excel应用
	workbook_source.close()
	app_source.quit()

	#进度条更新为10%
	progress_bar['value'] = 10
	progress_bar.update()


	#从dataframe中提取需要的列,并合并成一个表格
	df_s1 = df_sourcesheet1.iloc[:,[1,2,23,24,25,26,27,28,29,30,31,32,33]]
	df_s2 = df_sourcesheet2.iloc[:,[1,2,23,24,25,26,27,28,29,30,31,32,33]]
	# 重新定义列序号
	df_s1.columns = [0,1,2,3,4,5,6,7,8,9,10,11,12]
	df_s2.columns = [0,1,2,3,4,5,6,7,8,9,10,11,12]
	df_source = pd.concat([df_s1,df_s2],axis = 0)
	#重置列序号
	df_source.reset_index(drop=True, inplace=True)

	#进度条更新为20%
	progress_bar['value'] = 20
	progress_bar.update()





###############################################################################################################
#读取明细表信息，通过比对物料编码，在明细表中填上需要焊装信息
#将填好的excel另存到输出目录
###############################################################################################################

	#读取明细表，实例化
	app_MX = xw.App(visible = False, add_book=False)
	workbook_MX = app_MX.books.open(path_inputfile)

	#读明细表的页面，若无发读取则退出
	try:
		#读取明细文件的两个表格
		worksheet_MXT = workbook_MX.sheets(name_MXT)
		worksheet_MXB = workbook_MX.sheets(name_MXB)
		df_MXsheetT = pd.DataFrame(worksheet_MXT.range('A1').expand('table').value)
		df_MXsheetB = pd.DataFrame(worksheet_MXB.range('A1').expand('table').value)
	except:
		msg.showinfo('错误提示', '明细表格式错误！')
		#进度条更新为0%
		progress_bar['value'] = 0
		progress_bar.update()
		workbook_MX.close()
		app_MX.quit()
		return


	#删除标题
	df_MXsheetT = df_MXsheetT.drop(0)
	df_MXsheetT.reset_index(drop = True, inplace = True)
	df_MXsheetB = df_MXsheetB.drop(0)
	df_MXsheetB.reset_index(drop = True, inplace = True)

	#提取明细表的物料编码列
	wlbm_MXT = df_MXsheetT[1]
	wlbm_MXB = df_MXsheetB[1]


	#比较物料编码，从库里提取需要的列，改变表头
	df_MXT = comp_numb(wlbm_MXT,df_source)
	df_MXB = comp_numb(wlbm_MXB,df_source)

	df_MXT_toMX = df_MXT.iloc[:,[2,3,4,5,6,7,8,9,10,11,12]]
	df_MXB_toMX = df_MXB.iloc[:,[2,3,4,5,6,7,8,9,10,11,12]]
	df_MXT_toMX.columns = ['极性检查','三防','固封','成形','X光检查','焊接方式','烘烤','去金','搪锡','清洗','非功能螺钉']
	df_MXB_toMX.columns = ['极性检查','三防','固封','成形','X光检查','焊接方式','烘烤','去金','搪锡','清洗','非功能螺钉']

	#将提取的表格写入到明细表中
	worksheet_MXT.range('J1').options(pd.DataFrame, index=False).value = df_MXT_toMX
	worksheet_MXB.range('J1').options(pd.DataFrame, index=False).value = df_MXB_toMX

	#保存增加焊装信息的明细表,若有错误退出
	try:
		workbook_MX.save(path_MXout)
	except:
		msg.showinfo('错误提示', '目标文件已打开，请关闭！\n明细表无法增加信息保存！')
		#进度条更新为0%
		progress_bar['value'] = 0
		progress_bar.update()
		workbook_MX.close()
		app_MX.quit()
		return

	workbook_MX.close()
	app_MX.quit()

	#进度条更新为40%
	progress_bar['value'] = 40
	progress_bar.update()


###############################################################################################################
#将明细表和焊装信息合并
#将顶层明细和底层明细表合并
#排序合并表格
#表格分类
###############################################################################################################
	#从明细表中提取有用的列，形成新的表格
	df_MXsheetT = df_MXsheetT.iloc[:,[0,1,7]]
	df_MXsheetB = df_MXsheetB.iloc[:,[0,1,7]]


	#将顶层和底层的明细表合并成一个表格
	df_MXT = pd.concat([df_MXsheetT,df_MXT],axis = 1)
	df_MXB = pd.concat([df_MXsheetB,df_MXB],axis = 1)
	df_MX = pd.concat([df_MXT,df_MXB],axis = 0)
	df_MX.columns = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
	df_MX[3] = 1
	df_MX = df_MX.iloc[:,[1,4,2,0,3,5,6,7,8,9,10,11,12,13,14,15]]
	df_MX.columns = ['物料编码','元器件名称','型号规格','位号','单台数量','极性检查','三防',\
		'固封','成形','X光检查','焊接方式','烘烤','是否去金','是否搪锡','清洗','非功能螺钉']
	#按物料编码升序排列，并重新排列索引
	df_MX.sort_values(['型号规格','位号'], ascending = True, inplace = True)
	df_MX.reset_index(drop = True, inplace = True)
	#获取明细表长度
	len_MX = len(df_MX['型号规格'])



###############################################################################################################
#测试输出的excel
###############################################################################################################

	# app_CS = xw.App(visible = False, add_book=False)
	# book1 = app_CS.books.add()
	# sheet1 = book1.sheets['Sheet1']
	# sheet1.range('A1').value = df_MXT
	# book1.save('E:/code/SplitList_513/code_try/3.xlsx')
	# book1.close()
	# app_CS.quit()

	#根据物料编码汇总合并明细表，数量相加，位号和在一起
	n = 1
	m = 0
	while n < len_MX:
		if df_MX['型号规格'][n] == df_MX['型号规格'][m]:
			#对满足条件的位置赋值
			df_MX.loc[m,'位号'] = df_MX.loc[m,'位号'] + ',' + df_MX.loc[n,'位号']
			df_MX.loc[m,'单台数量'] = df_MX.loc[m,'单台数量']  + df_MX.loc[n,'单台数量']
			#删除第n列
			df_MX=df_MX.drop(n)
		else:
			m = n
		n = n + 1
	#序号重新排序
	df_MX.reset_index(drop = True, inplace = True)


	#进度条更新为50%
	progress_bar['value'] = 50
	progress_bar.update()

###############################################################################################################
#将汇总好的表格分类成需要的几种BOM
#新建输出的BOM表
###############################################################################################################
	#print(df_MX[df_MX['焊接方式'].isin([None, '待定','无效','SMT'])])

	app_BOM = xw.App(visible = False, add_book=False)
	workbook_BOM = app_BOM.books.open(path_BOMfile)

	#判断不在BOM的清单是否已建立，未建立则建立，已建立则打开
	try:
		workbook_NBOM = app_BOM.books.add()
		workbook_NBOM.save(path_NBOMout)
	except:
		workbook_NBOM = app_BOM.books.open(path_NBOMout)

	#定义BOM的页面便量，若不满足，则提示关闭退出
	try:
		worksheet_BOM_SMT = workbook_BOM.sheets('表贴器件BOM')
		worksheet_BOM_FL = workbook_BOM.sheets('分立器件BOM')
		worksheet_BOM_NSF = workbook_BOM.sheets('非三防器件BOM')
		worksheet_BOM_DF = workbook_BOM.sheets('点封器件BOM')
	except:
		msg.showinfo('错误提示', 'BOM文件格式错误！')
		#进度条更新为0%
		progress_bar['value'] = 0
		progress_bar.update()
		workbook_BOM.close()
		workbook_NBOM.close()
		app_BOM.quit()
		return
	worksheet_NBOM = workbook_NBOM.sheets[0]
	worksheet_NBOM.name = '不在BOM清单中的元器件'

	#生成SMT表格
	df_BOM = df_MX[df_MX['焊接方式'].isin(['SMT'])]
	df_BOM = df_BOM.loc[:,['元器件名称','型号规格','单台数量','是否去金','是否搪锡']]
	df_BOM.insert(2,'位号','/')
	df_BOM.insert(4,'烘烤参数(℃，h)','')
	df_BOM.insert(5,'成形参数/内，外间(mm)','')
	df_BOM.reset_index(drop = True, inplace = True)
	df_BOM.index = df_BOM.index + 1
	worksheet_BOM_SMT.range('A5').options(pd.DataFrame, index = True, header = False).value = df_BOM
	len_BOM = len(df_BOM['型号规格'])

	#设定调格式的范围，并调用函数调整格式
	range_BOM = worksheet_BOM_SMT.range((5,1),(len_BOM + 4,10))
	formatset_BOM(range_BOM)

	#进度条更新为60%
	progress_bar['value'] = 60
	progress_bar.update()


	#生成分立表格
	df_BOM = df_MX[~df_MX['焊接方式'].isin([None, '待定','无效','SMT'])]
	df_BOM = df_BOM.loc[:,['元器件名称','型号规格','位号','单台数量','是否去金','是否搪锡','焊接方式']]
	df_BOM.insert(4,'成形参数/中心距(mm)','')
	df_BOM.insert(5,'剪腿参数/距离本体(mm)','')
	df_BOM.reset_index(drop = True, inplace = True)
	df_BOM.index = df_BOM.index + 1
	worksheet_BOM_FL.range('A5').options(pd.DataFrame, index = True, header = False).value = df_BOM
	len_BOM = len(df_BOM['型号规格'])

	#设定调格式的范围，并调用函数调整格式
	range_BOM = worksheet_BOM_FL.range((5,1),(len_BOM + 4,12))
	formatset_BOM(range_BOM)

	#进度条更新为70%
	progress_bar['value'] = 70
	progress_bar.update()

	#生成不在BOM里的元器件
	df_BOM = df_MX[df_MX['焊接方式'].isin([None, '待定','无效'])]
	df_BOM = df_BOM.loc[:,['元器件名称','型号规格','位号','单台数量','是否去金','是否搪锡','焊接方式']]
	df_BOM.reset_index(drop = True, inplace = True)
	df_BOM.index = df_BOM.index + 1

	worksheet_NBOM.range('A1').options(pd.DataFrame, index = True, header = True).value = df_BOM

	#进度条更新为80%
	progress_bar['value'] = 80
	progress_bar.update()


	#生成不用三防的元器件
	df_BOM = df_MX[df_MX['三防'].isin([0])]
	df_BOM = df_BOM.loc[:,['元器件名称','型号规格','位号','单台数量']]
	df_BOM.reset_index(drop = True, inplace = True)
	df_BOM.index = df_BOM.index + 1
	worksheet_BOM_NSF.range('A5').options(pd.DataFrame, index = True, header = False).value = df_BOM
	len_BOM = len(df_BOM['型号规格'])

	#设定调格式的范围，并调用函数调整格式
	range_BOM = worksheet_BOM_NSF.range((5,1),(len_BOM + 4,9))
	formatset_BOM(range_BOM)

	#进度条更新为90%
	progress_bar['value'] = 90
	progress_bar.update()

	#生成点封的元器件
	df_BOM = df_MX[df_MX['固封'].isin([1])]
	df_BOM = df_BOM.loc[:,['元器件名称','型号规格','位号','单台数量']]
	df_BOM.reset_index(drop = True, inplace = True)
	df_BOM.index = df_BOM.index + 1
	worksheet_BOM_DF.range('A5').options(pd.DataFrame, index = True, header = False).value = df_BOM
	len_BOM = len(df_BOM['型号规格'])

	#设定调格式的范围，并调用函数调整格式
	range_BOM = worksheet_BOM_DF.range((5,1),(len_BOM + 4,9))
	formatset_BOM(range_BOM)

	try:
		workbook_BOM.save(path_BOMout)
		workbook_NBOM.save(path_NBOMout)
	except:
		msg.showinfo('错误提示', '目标文件已打开，请关闭！\nBOM文件无法保存！')
		#进度条更新为0%
		progress_bar['value'] = 0
		progress_bar.update()
		workbook_BOM.close()
		workbook_NBOM.close()
		app_BOM.quit()
		return

	workbook_BOM.close()
	workbook_NBOM.close()
	app_BOM.quit()

	#进度条更新为100%
	progress_bar['value'] = 100
	progress_bar.update()



	msg.showinfo('完成提示', '一生二，二生三，三生万物！')

	#进度条更新为0%
	progress_bar['value'] = 0
	progress_bar.update()





def main():
	path_sourcefile = 'E:/code/SplitList_513/code_try/库(公开).xls'
	#path_inputfile = 'E:/code/SplitList_513/code_try/XA1明细表(公开).xls'
	#path_inputfile = 'E:/code/SplitList_513/code_try/电源板明细表(公开).xls'
	#path_inputfile = 'E:/code/SplitList_513/code_try/XC1明细表(公开).xls'
	#path_inputfile = 'E:/code/SplitList_513/code_try/基带板明细表(公开).xls'
	path_inputfile = 'E:/code/SplitList_513/code_try/基带板明细表2(公开).xls'
	path_BOMfile = 'E:/code/SplitList_513/code_try/BOM汇总 新（公开）.xlsx'
	path_outputforder = 'E:/code/SplitList_513/code_try/输出'
	Split_MX(path_sourcefile,path_inputfile,path_BOMfile,path_outputforder)

if __name__ == '__main__':
	main()